Bug 3495

Bug Description:

This is a race.
Java.lang.ClassLoader is not thread safe when two or more threads try to load the same class simultaneously. It throws a LinkageError exception.
An exception thrown by this bug:

  Exception in thread "Thread-0" java.lang.LinkageError: loader (instance of org/codehaus/groovy/tools/RootLoader):
attempted duplicate class definition for name: "groovy/beans/Bindable"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at org.codehaus.groovy.tools.RootLoader.oldFindClass(RootLoader.java:152)
at org.codehaus.groovy.tools.RootLoader.loadClass(RootLoader.java:124)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
at org.codehaus.groovy.tools.Groovy3495$1.run(Groovy3495.java:34)
More details about this bug are at GROOVY-3495 JIRA page.

Interleaving Description:

The numbers in the following code snippet present the global execution sequence.

org.codehaus.groovy.tools.Rootloader

t1    t2    protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
1     3         Class c = this.findLoadedClass(name);
                if (c != null) return c;
                c = (Class) customClasses.get(name);
2               if (c != null) return c;
            
                try {
5     4             c = oldFindClass(name);
                } catch (ClassNotFoundException cnfe) {
                    // IGNORE
                }
                ...
            }
        
Precondition: thread1 and thread2 both try to load the class "groovy/beans/Bindable"

a) thread1 executes 1 to check whether the given class was loaded before.
b) the given class was not loaded and thread1 goes 2. Now c is null.
c) after thread1 executes 2, context switches and thread2 executes 3. The method findLoadedClass returns null, since the given class has not been loaded in thread1.
d) then thread2 execute 4 to load the class.
e) after thread2 executes 4, context switches, thread1 continues 5 to try to load the class which has been loaded in thread2, and a LinkageError exception is thrown in thread1.

How To Reproduce:

This bug is reproduced under groovy 1.7.9 and JDK 1.6.0_33.
Execute the following scripts to run the test to reproduce the bug (assume the location of the groovy test project is groovy_test_home).

Linux:
${groovy_test_home}/scripts/run3495.sh [--threadnum arg]
Windows:
%groovy_test_home%\scripts\run3495.bat [--threadnum arg]

The default number of threads is 3 if not specified. For example,
${groovy_test_home}/scripts/run3495.sh
is the same as
${groovy_test_home}/scripts/run3495.sh --threadnum 3

Option Function Default Value Valid Values
--threadnum,-tn number of threads. 3 integer